﻿Imports ActiveDs
Imports System.DirectoryServices
Imports System.Security.Principal

''' <summary>
''' グループリスト画面のフォームです。
''' </summary>
''' <remarks></remarks>
Public Class GroupList
  Inherits Page

#Region " プライベートフィールド "
  'プライマリ グループのメンバー格納用(グループのプライマリ グループ トークン, メンバーのパスとオブジェクトの種類のコレクション)
  Private primaryGroupMembers As Dictionary(Of Integer, Dictionary(Of String, CategoryType))
#End Region

#Region " イベントハンドラ "
  Private Sub GroupsDataSource_Selected(
    sender As Object, e As ObjectDataSourceStatusEventArgs) Handles GroupsDataSource.Selected

    Me.CountLabel.Text = String.Format("{0} 個のオブジェクト", DirectCast(e.ReturnValue, ICollection).Count)
  End Sub

  Private Sub DetailDataSource_Selecting(
      sender As Object, e As ObjectDataSourceSelectingEventArgs) Handles DetailDataSource.Selecting

    If Me.GroupListBox.SelectedIndex = -1 Then
      e.Cancel = True
    End If
  End Sub

  Private Sub DetailDataSource_Selected(
    sender As Object, e As ObjectDataSourceStatusEventArgs) Handles DetailDataSource.Selected

    If ViewState("primaryGroupMembers") Is Nothing Then
      primaryGroupMembers = New Dictionary(Of Integer, Dictionary(Of String, CategoryType))()
      ViewState("primaryGroupMembers") = primaryGroupMembers
      Me.RelatingPanel.Visible = True
    Else
      primaryGroupMembers = DirectCast(
        ViewState("primaryGroupMembers"), Dictionary(Of Integer, Dictionary(Of String, CategoryType)))
    End If

    Dim group = DirectCast(e.ReturnValue, DomainGroup)
    Me.ShowMembers(group)   'メンバーを表示
    Me.ShowBelongGroups(group.Entry)  '所属するグループを表示

    Me.InRoleButton.Enabled = Me.GroupGridView.Rows.Count > 0
    If Me.InRoleButton.Enabled Then
      Me.InRoleButton.OnClientClick = String.Format(
        "showInRoleGroupList('{0}', '{1}')", Me.GroupListBox.SelectedValue, CategoryType.Group)
    End If
    Me.InRoleUpdatePanel.Update()

    Me.MemberButton.Enabled = Me.MemberGridView.Rows.Count > 0
    If Me.MemberButton.Enabled Then
      Me.MemberButton.OnClientClick = String.Format("showAllMemberList('{0}')", Me.GroupListBox.SelectedValue)
    End If
    Me.MemberUpdatePanel.Update()
  End Sub

  Protected Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    Response.Redirect("Main.aspx?idx=1")
  End Sub
#End Region

#Region " プライベートメソッド "
  ''' <summary>
  ''' メンバーを表示します。
  ''' </summary>
  ''' <param name="group">グループ。</param>
  ''' <remarks></remarks>
  Private Sub ShowMembers(group As DomainGroup)
    Dim table = CreateDataSourceTable()   'データソース用のテーブルを作成
    Dim objectType As CategoryType    'ディレクトリ オブジェクトの種類

    For Each member As IADs In group.Native.Members()
      Dim row = table.NewRow()
      objectType = DirectCast([Enum].Parse(GetType(CategoryType), member.Class, True), CategoryType)
      If objectType = CategoryType.ForeignSecurityPrincipal Then  '外部のセキュリティ プリンシパルの時
        Dim objectSid = DirectCast(member.Get("objectSid"), Byte())
        Dim sid = New SecurityIdentifier(objectSid, 0)  'SID
        Dim account = DirectCast(sid.Translate(GetType(NTAccount)), NTAccount)  'アカウントに変換
        row.Item(0) = account.Value   'NT AUTHORITY\○○ => ToStringメソッドでも同じ
      Else  '外部のセキュリティプリンシパル以外の時
        row.Item(0) = member.Get("cn").ToString()
      End If
      row.Item(1) = DirectoryAccess.GetBelongPath(member)   '所属パスを取得
      table.Rows.Add(row)
    Next
    Me.AddPrimaryGroupMembers(group, table)   'プライマリ グループになっているメンバを追加

    Me.MemberGridView.DataSource = New DataView(table) With {.Sort = "Name"}
    Me.MemberGridView.DataBind()
  End Sub

  ''' <summary>
  ''' データソース用のテーブルを作成します。
  ''' </summary>
  ''' <returns>データソース用のテーブル。</returns>
  ''' <remarks></remarks>
  Private Function CreateDataSourceTable() As DataTable
    Dim table = New DataTable()
    table.Columns.Add(New DataColumn("Name", GetType(String)))
    table.Columns.Add(New DataColumn("Path", GetType(String)))
    Return table
  End Function

  ''' <summary>
  ''' プライマリ グループになっているメンバを追加します。
  ''' </summary>
  ''' <param name="group">グループ。</param>
  ''' <param name="table">メンバのバインド用のテーブル。</param>
  ''' <remarks></remarks>
  Private Sub AddPrimaryGroupMembers(group As DomainGroup, table As DataTable)
    Dim token = DirectoryAccess.GroupTokens.Single(Function(item) item.Value.Equals(group.Name)).Key  'グループのプライマリ グループ トークン
    'プライマリ グループのメンバー(パスとオブジェクトの種類のコレクション)
    Dim primaryMembers As Dictionary(Of String, CategoryType) = Nothing

    If primaryGroupMembers.TryGetValue(token, primaryMembers) Then  '格納済の時
      If primaryMembers.Count = 0 Then
        Return
      End If
    Else    '未格納の時
      'プライマリ グループ トークンのグループをプライマリ グループとしているメンバのDirectoryEntryのコレクションを取得
      Dim primaryMemberEntries = DirectoryAccess.GetPrimaryGroupMemberEntries(token)
      primaryMembers = New Dictionary(Of String, CategoryType)(primaryMemberEntries.Count)
      primaryGroupMembers.Add(token, primaryMembers)
      If primaryMemberEntries.Count = 0 Then
        Return
      End If

      For Each entry In primaryMemberEntries  '取得メンバー数分
        primaryMembers.Add(entry.Path,
          DirectCast([Enum].Parse(GetType(CategoryType), entry.SchemaClassName, True), CategoryType))
        entry.Close()
      Next
    End If

    For Each member In primaryMembers   'プライマリ グループになっているメンバー数分
      Dim row = table.NewRow()
      row.Item(0) = DirectoryAccess.PathToCn(member.Key)  '名前を取得
      row.Item(1) = DirectoryAccess.GetBelongPath(member.Key)   '所属パスを取得
      table.Rows.Add(row)
    Next
  End Sub

  ''' <summary>
  ''' 所属するグループを表示します。
  ''' </summary>
  ''' <param name="group">グループ。</param>
  ''' <remarks></remarks>
  Private Sub ShowBelongGroups(group As DirectoryEntry)
    Dim table = CreateDataSourceTable()   'データソース用のテーブルを作成
    For Each groupPath As String In group.Properties.Item("memberOf")
      Dim row = table.NewRow()
      row.Item(0) = DirectoryAccess.PathToCn(groupPath)   '名前を取得
      row.Item(1) = DirectoryAccess.GetBelongPath(groupPath)  '所属パスを取得
      table.Rows.Add(row)
    Next

    Me.GroupGridView.DataSource = New DataView(table) With {.Sort = "Name"}
    Me.GroupGridView.DataBind()
  End Sub
#End Region
End Class